/* $RCSFile: InitApplication.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/ADNCRM.Core/Configuration/InitApplication.cs $
 * $log$
 * Revision 19 2010/08/16 22:52:09  christian.surieux
 *   Suppress telerik version from web.config, Add comments for connection string, Add dbo. to adn_version table creation source in DBDataStore
 * Revision 6 2010/08/06 19:56:27  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Web;
using System.Net;
using System.Web.Configuration;
using System.Globalization;

using AddonNice.Diagnostics;
using AddonNice.Security;
using AddonNice.Settings;
using Microsoft.Win32;


namespace AddonNice.Configuration
{
	/// <summary>
	/// Class used for initialization of Application, called in Application_Start
	/// </summary>
	public class InitApplication
	{
		static 	public Hashtable InitObjects		    =	new Hashtable();
		static 	public Monitoring MonInitObject			=	null;
		static 	public bool	IsIIS7			            =	false;
		static 	public bool	IsIIS7_IP			        =	false;
        
        private static bool s_InitializedAlready        =   false;
        private static Object s_lock                    =   new Object();

        public bool InitializedAlready
        {
            get
            {
                return s_InitializedAlready;
            }
        }

        /// <summary>
        /// This is called on real App_Start, as far as new IIS7 Integrated Pipeline starts app when Context.Request is not available (beware Context is not null)
        /// We must initialise in 2 parts
        /// First part only starts tracelistener
        /// </summary>
		public void Start()
        {
            // Start the tracing system
            ADNCRMTraceListener.InitSorage(Config.TracePackHeader, Config.TraceVerbose, Config.TraceIndent);
            HttpContext context     =   HttpContext.Current;
            if ( IsIIS7Installed() )
            {
                IsIIS7              =   true;
                // now test the integrated pipeline mode / classic mode
                try
                {
                    HttpRequest req =   HttpContext.Current.Request;
                }
                catch
                {
                    IsIIS7_IP       =   true;
                }
            }
            AddonNice.Settings.Cache.CurrentCache.RemoveItems       +=   new AddonNice.Settings.Cache.RemoveDelegate(Helpers.CacheHelper.Remove);
            AddonNice.Settings.Cache.CurrentCache.useRemoveItems    =   true;
            if (GlobalTraceSwitch.Sw.TraceVerbose)
                Trace.WriteLine(
                    string.Format("[V]InitApplication StartIIS7IP IIS7 IP: {0}, UseProxyServerForServerWebRequests: {1}, IsMonitoringEnabled: {2}, AuthenticationMode: {3}, EnableLogUserVisit: {4}.",
                    new object[]{IsIIS7_IP,Config.UseProxyServerForServerWebRequests,Config.IsMonitoringEnabled,
                            Config.AuthenticationMode,Config.EnableLogUserVisit}), GlobalTraceSwitch.Sw.Info);
        }
            
		/// <summary>
        /// Second Init part
		/// Initialize AddonNice Application by :
		/// - starting traces
		/// - initializing the codeversions and databases versions
		/// - check for disk access for aspnet user in portal folder
		/// - setting magic urls in application
        /// Returns true if the call has done the first initialization
		/// </summary>
        public bool StartWithContext(HttpContext context)
		{
            if (s_InitializedAlready)
            {
                return false;
            }
            lock (s_lock)
            {
                if (s_InitializedAlready)
                {
                    return false;
                }
                // Perform first-request initialization here ...
                s_InitializedAlready    =   true;

                // Install versions testing
                int ver                 =   PortalSettings.CodeVersion;
                AddonNice.Settings.DatabaseDB.ClearDatabaseVersion();

                if (GlobalTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication StartWithContext BEG Code version: {0}",ver), GlobalTraceSwitch.Sw.Info);
                // check for file access
                if (Config.CheckForFilePermission)
                {
                    string myNewDir     =   System.IO.Path.Combine(AddonNice.Settings.Path.ApplicationPhysicalPath, "_createnewdir");
                    if (GlobalTraceSwitch.Sw.TraceVerbose)
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication StartWithContext Check Executing user file access myNewDir: '{0}'",myNewDir), GlobalTraceSwitch.Sw.Info);
                    // Verify if executing user has rights on application folder
                    try
                    {
                        if (!Directory.Exists(myNewDir))
                            Directory.CreateDirectory(myNewDir);
                        if (Directory.Exists(myNewDir))
                            Directory.Delete(myNewDir);
                    }
                    catch (Exception ex)
                    {
                        if (GlobalTraceSwitch.Sw.TraceError)
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]InitApplication Start ASPNET Account does not have rights to the filesystem ex : {0}", ex.Message), GlobalTraceSwitch.Sw.Info);
                        context.Response.Redirect("~/app_support/ErrorNoFilePermission.html", false);
                    }
                }

                // install magic urls
                try
                {
                    if (GlobalTraceSwitch.Sw.TraceVerbose)
                        Trace.WriteLine("[V]InitApplication StartWithContext Read magic urls from DB.", GlobalTraceSwitch.Sw.Info);
                    DataTable tb                    =   new MagicUrlsDB().GetAllMagicUrlsGlobalized();
                    context.Application.Lock();
                    context.Application["MagicUrls"]=  tb;
                }
                catch (Exception ex)
                {
                    if (GlobalTraceSwitch.Sw.TraceError)
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]InitApplication StartWithContext Read magicUrls ex: {0}", ex), GlobalTraceSwitch.Sw.Info);
                }
                finally
                {
                    context.Application.UnLock();
                }

                if (Config.UseProxyServerForServerWebRequests)
                {
                    if (GlobalTraceSwitch.Sw.TraceVerbose)
                        Trace.WriteLine("[V]InitApplication StartWithContext UseProxyServerForServerWebRequests.", GlobalTraceSwitch.Sw.Info);
                    WebRequest.DefaultWebProxy  =   PortalSettings.GetProxy();
                    if (GlobalTraceSwitch.Sw.TraceVerbose)
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication StartWithContext DefaultWebProxy: '{0}'",WebRequest.DefaultWebProxy), GlobalTraceSwitch.Sw.Info);
                }
                if (Config.IsMonitoringEnabled)
                {
                    if (GlobalTraceSwitch.Sw.TraceVerbose)
                        Trace.WriteLine("[V]InitApplication StartWithContext MonitoringEnabled.", GlobalTraceSwitch.Sw.Info);
                    MonInitObject               =   new Monitoring();
                }
                LoadInitObjects();
                if (GlobalTraceSwitch.Sw.TraceVerbose)
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication StartWithContext END Code version: {0}",ver), GlobalTraceSwitch.Sw.Info);
            }
            return true;
		}

		/// <summary>
		/// Read the Config Section named InitSubSystem and load the corresponding assemblies
		/// Instanciate the object with a class name specified as second parameter
		/// </summary>
		public void LoadInitObjects()
		{
			if ( GlobalTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine("[V]InitApplication LoadInitObject BEG.",GlobalTraceSwitch.Sw.Info);
			// Get custom config
            InitSubSystem mySettings        =   (InitSubSystem)WebConfigurationManager.GetSection("InitSubSystem");
            try
			{
				if ( mySettings != null )
				{
					string appPath			=   HttpRuntime.AppDomainAppVirtualPath.Equals("/") ? string.Empty : HttpRuntime.AppDomainAppVirtualPath;
					foreach(InitData ObjectType in mySettings.InitClasses)
					{
						if ( GlobalTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication LoadInitObject Product: {0}.",ObjectType.Product),GlobalTraceSwitch.Sw.Info);
						string className	=	ObjectType.InitClass.Trim();
						string assembly		=	ObjectType.LoadAssembly.Trim();

						//Instantiate the InitObjInst and add it to the list
						LoadInitObject(appPath,ObjectType.Product,className, assembly);
					}
				}
			}
			catch(Exception exp)
			{
				if ( GlobalTraceSwitch.Sw.TraceError )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]InitApplication LoadInitObject ex: {0}",exp),GlobalTraceSwitch.Sw.Info);
				throw;
			}
			if ( GlobalTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine("[V]InitApplication LoadInitObject END.",GlobalTraceSwitch.Sw.Info);
		}

		/// <summary>
		/// Load and store the Init object in the internal hashtable for persistence
		/// The InitObject classes must define a static object that will do any necessary initialization work for the extension application
		/// </summary>
		public void LoadInitObject(string appPath,string Product,string className, string assembly)
		{
			if ( GlobalTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication LoadInitObject BEG className: {0} assembly: {1}.",className,assembly),GlobalTraceSwitch.Sw.Info);
			//Search for assembly in bin dir
			string assemblyName		=   HttpRuntime.BinDirectory + assembly;
			// Use reflection for loading assembly
			Assembly a				=   null;
            if (assembly.ToLower().IndexOf("addonnice.dll") > -1)
            {
                a                   =   Assembly.GetExecutingAssembly();
   			    if ( GlobalTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication LoadInitObject GetExecuting assembly from current assembly className: {0}, assemblyname: {1}, get ok: {2}",className,assemblyName,(a != null)),GlobalTraceSwitch.Sw.Info);
            }
            else
            {
                a                   =   Assembly.LoadFrom(assemblyName);
                if ( GlobalTraceSwitch.Sw.TraceVerbose )
                    Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication LoadInitObject className: {0} from assembly with assemblyname: {1}, get ok: {2}", className, assemblyName, ( a != null )),GlobalTraceSwitch.Sw.Info);
            }

			// Use reflection to create an InitObjInst instance
            if (a != null)
            {
                object InitObjInst  =   null;
                try
                {
                    if ( GlobalTraceSwitch.Sw.TraceVerbose )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication LoadInitObject className: {0} assembly: {1} trying createInstance", className, assembly),GlobalTraceSwitch.Sw.Info);
                    InitObjInst     =   a.CreateInstance(className);
                    if (InitObjInst != null)
                    {
                        if ( GlobalTraceSwitch.Sw.TraceVerbose )
                            Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication LoadInitObject className: {0} assembly: {1} added to Static objects list", className, assembly),GlobalTraceSwitch.Sw.Info);
                        InitObjects.Add(Product, InitObjInst);
                    }
                }
                catch (Exception ex)
                {
                    if ( GlobalTraceSwitch.Sw.TraceError )
                        Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[E]InitApplication LoadInitObject className: {0} assembly: {1}, ex: {2}", className, assembly, ex),GlobalTraceSwitch.Sw.Info);
                }
            }
			if ( GlobalTraceSwitch.Sw.TraceVerbose )
                Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"[V]InitApplication LoadInitObject END className: {0} assembly: {1}.",className,assembly),GlobalTraceSwitch.Sw.Info);
		}

        #region class-wide fields

        const string IISRegKeyName = "Software\\Microsoft\\InetStp";
        const string IISRegKeyValue = "MajorVersion";
        const string IISRegKeyMinorVersionValue = "MinorVersion";

        #endregion

        #region IsIISInstalled functions

        #region GetRegistryValue
        private static bool GetRegistryValue<T>(RegistryHive hive, string key, string value, RegistryValueKind kind, out T data)
        {
            bool success = false;
            data = default(T);

            using (RegistryKey baseKey = RegistryKey.OpenRemoteBaseKey(hive, String.Empty))
            {
                if (baseKey != null)
                {
                    using (RegistryKey registryKey = baseKey.OpenSubKey(key, RegistryKeyPermissionCheck.ReadSubTree))
                    {
                        if (registryKey != null)
                        {
                            try
                            {
                                // If the key was opened, try to retrieve the value.
                                RegistryValueKind kindFound = registryKey.GetValueKind(value);
                                if (kindFound == kind)
                                {
                                    object regValue = registryKey.GetValue(value, null);
                                    if (regValue != null)
                                    {
                                        data = (T)Convert.ChangeType(regValue, typeof(T), CultureInfo.InvariantCulture);
                                        success = true;
                                    }
                                }
                            }
                            catch (IOException)
                            {
                                // The registry value doesn't exist. Since the
                                // value doesn't exist we have to assume that
                                // the component isn't installed and return
                                // false and leave the data param as the
                                // default value.
                            }
                        }
                    }
                }
            }
            return success;
        }
        #endregion


        #region IsIIS7Installed
        private static bool IsIIS7Installed()
        {
            bool found = false;
            int regValue = 0;

            if (GetRegistryValue(RegistryHive.LocalMachine, IISRegKeyName, IISRegKeyValue, RegistryValueKind.DWord, out regValue))
            {
                if (regValue == 7)
                {
                    found = true;
                }
            }

            return found;
        }
        #endregion

        #endregion
    }
}
